function observe(value, m) {
    if (!value || typeof value !== 'object') {
        return
    }
    return new Observer(value)
}

class Dep {
    static uid = 0
    static target = null;

    constructor() {
        this.id = Dep.uid++
        this.subs = []
    }

    addSub(sub) {
        this.subs.push(sub)
    }

    depend() {
        Dep.target.addDep(this)
    }

    removeSub(sub) {
        let index = this.subs.indexOf(sub)
        if (index !== -1) {
            this.subs.splice(index, 1)
        }
    }

    notify() {
        this.subs.forEach((sub) => {
            sub.update()
        })
    }
}

class Observer {
    constructor(data) {
        this.data = data
        this.walk(data)
    }

    walk(data) {
        let that = this
        Object.keys(data).forEach((key) => {
            this.convert(key, data[key])
        })
    }

    convert(key, val) {
        this.defineReactive(this.data, key, val)
    }

    defineReactive(data, key, val) {
        let dep = new Dep()
        let childObj = observe(val)
        Object.defineProperty(data, key, {
            enumerable: true,
            configurable: false,
            get() {
                if (Dep.target) {
                    dep.depend()
                }
                return val
            },
            set(newVal) {
                if (newVal === val) {
                    return
                } else {
                    val = newVal
                    childObj = observe(newVal)
                    dep.notify()
                }
            }
        })
    }
}
